package com.briup.es.service.impl;

import com.briup.es.config.auth.UserDetails;
import com.briup.es.config.response.ResultCode;
import com.briup.es.domain.bean.Order;
import com.briup.es.domain.bean.Shopcar;
import com.briup.es.domain.dto.AddOrderDto;
import com.briup.es.domain.vo.OrderDetailVO;
import com.briup.es.domain.vo.OrderItemVO;
import com.briup.es.domain.vo.OrderVO;
import com.briup.es.mapper.OrderMapper;
import com.briup.es.mapper.ShopcarMapper;
import com.briup.es.service.BookService;
import com.briup.es.service.OrderItemService;
import com.briup.es.service.OrderService;
import com.briup.es.utils.AssertUtil;
import com.briup.es.utils.BeanUtil;
import lombok.SneakyThrows;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;

@Service
public class OderServiceImpl implements OrderService {

    @Resource
    private OrderMapper orderMapper;
    @Resource
    private ShopcarMapper shopcarMapper;
    @Resource
    private OrderItemService orderItemService;
    @Resource
    private BookService bookService;

    @Override
    public List<OrderVO> queryAllByConditional(String id, Integer status) {
        // 查询当前用户下的所有符合条件的订单
        Integer userId = UserDetails.userId();
        // 在根据当前用户id去订单表查询符合条件的数据
        return orderMapper.queryAllByConditional(userId, id, status);
    }

    // 对于多个增删改操作组成的综合逻辑，需要对其进行事务控制
    @Transactional
    public void generatorOrder(AddOrderDto orderDto) {
        // 校验购物车的购物车项是否还存在
        List<Integer> shopCarIds = orderDto.getShopCarIds();
        // 购物车项id集合不能为空
        AssertUtil.isTrue(!CollectionUtils.isEmpty(shopCarIds),
                ResultCode.SHOPCAR_ID_IS_EMPTY);
        // 去购物车表中查询购物车项的数据
        List<Shopcar> shopcars = shopcarMapper.queryAllByIds(shopCarIds);
        // 校验数据库中查询的购物车项个数与传入的购物车项id数目是否相同
        AssertUtil.isTrue(shopcars.size() == shopCarIds.size(),
                ResultCode.SHOPCAR_NUM_IS_ERROR);
        // 继续生成订单
        Order order = initOrder(orderDto);
        // 将订单数据存入数据库中
        orderMapper.addOrder(order);
        // 继续生成订单下的订单项
        orderItemService.generatorOrderItem(order.getId(), shopcars);
        // 生成完订单后，将购物车中对应的购物车项数据进行删除
        shopcarMapper.deleteBatch(shopCarIds);

        // 减少对应的商品数量
        // 从购物车中收集图书的id以及其需要修改的图书数量
        Map<Integer, Integer> map = shopcars.stream().collect(Collectors.toMap(
                Shopcar::getBookId,
                shopcar -> -shopcar.getNum()
        ));
        bookService.updateStoreNum(map);
    }

    @SneakyThrows
    @Override
    public OrderDetailVO getOrderDetail(String orderId) {
        // 分两步走，先去订单表中查询订单数据
        OrderVO orderVO = orderMapper.queryById(orderId);

        // 再根据订单id去订单项表中查询订单项的数据
        List<OrderItemVO> itemVOS = orderItemService.queryByOrderId(orderId);

        // 将orderVO 转换为 OrderDetailVO，再将 itemVOS放入 OrderDetailVO中
        OrderDetailVO detailVO = BeanUtil.bean2VO(orderVO, OrderDetailVO.class);
        detailVO.setOrderItems(itemVOS);

        return detailVO;
    }

    @Override
    @Transactional
    public void closeOrder(String orderId) {
        // 需要先修改订单的状态
        // 在修改之前先确定订单是否为已创建未支付状态，只有该状态的订单能够修改
        changOrderStatus(orderId, 1, 5);

        // 查询订单项的数据，将订单项对应的图书库存加回去
        List<OrderItemVO> itemVOS = orderItemService.queryByOrderId(orderId);
        // 将订单项数据转换为 图书id与购买数量的映射
        Map<Integer, Integer> map = itemVOS.stream().collect(Collectors.toMap(
                OrderItemVO::getBookId,
                OrderItemVO::getNum
        ));

        // 调用bookService中的方法，将订单中的图书库存添加回去
        bookService.updateStoreNum(map);
    }

    @Override
    public void completeOrder(String orderId) {
        // 校验订单状态，只有已发货为收货的订单才能确认收货
        changOrderStatus(orderId, 3, 4);
    }

    /**
     * 修改订单状态
     *
     * @param orderId      订单id
     * @param prefixStatus 修改状态为指定状态的前提状态
     * @param newStatus    订单的新状态
     */
    private void changOrderStatus(String orderId, int prefixStatus, int newStatus) {
        // 确定订单是否满足某一个状态条件
        int count = orderMapper.countStatusById(orderId, prefixStatus);
        AssertUtil.isTrue(count == 1,
                ResultCode.ORDER_STATUS_IS_NOT_SUPPORT);
        // 当订单的前置状态满足后，方能将订单修改为新的状态
        orderMapper.changeOrderStatus(orderId, newStatus);
    }

    /**
     * 初始化需要生成的订单详细
     *
     * @param orderDto 用户传入的订单详细
     * @return Order对象
     */
    private Order initOrder(AddOrderDto orderDto) {
        // 获取当前登录用户id
        Integer userId = UserDetails.userId();
        // 生成订单编号
        String orderId = String.format("briup%s",
                UUID.randomUUID().toString().replace("-", ""));
        // 以建造者模式创建Order类对象
        return Order.builder()
                .id(orderId)
                .userId(userId)
                // 创建时间设置为当前时间
                .createDate(LocalDateTime.now())
                // 订单状态默认为1
                .status(1)
                .receiveUsername(orderDto.getReceiveUsername())
                .receiveAddress(orderDto.getReceiveAddress())
                .receivePhone(orderDto.getReceivePhone())
                .build();
    }
}
